
package com.studiofortress.sf.util;

import java.util.Random;

/**
 * a linear random method based on xor shifts--which is a fast way to do LFSR
 * --ie one clock per bit is slow. This is faster per step that java Random.
 *
 * This does better than LCC generators (ie passes the monkey tests and DNA
 * tests where LCG dont). In other words it does not have the hyperplane
 * problem.
 *
 * This has a period of 2**128-1. This is quite easy to prove as follows.
 *
 * the counter can be shown to be a LFSR with period 2**64-1. However we have a
 * simple counter in the stepped variable. That is after 2**64 counts stepped
 * mod 2**64 == 0. Hence the phase is shifted by one and the period of stepped
 * and counter are relatively prime. We combine them with Addition, which is
 * slightly nonlinear due to carry. Of course we could just use a simple ++
 * counter. But thats boring.
 *
 * We could use * for this as well and have a stronger condition for non
 * lineararity.
 *
 * We speed up the nextDouble function as well.
 *
 * @author bob - http://www.javagaming.org/index.php/topic,18426.0.html
 */
final class Random64 extends Random
{
	private static final double LONG_2_DOUBLE =1.0 / (double)(1L<<53);
	private static final long MASK_53=(1l<<53)-1;
	private static final long serialVersionUID =-6678124822567014769L;

	private static final long PRIME =0xd4d6712ee634312dl;
	private long counter ;
	private long stepped ;

	public Random64() {
		super();
                setSeed(System.nanoTime());
	}

	public Random64(long seed) {
		super(seed);
		setSeed(seed);
	}

	private void step(){
		counter ^=(counter << 21);
		counter ^=(counter >>> 35);
		counter ^=(counter << 4);
		stepped +=PRIME;
	}
	/**
	 * could use all 64 bits over 2 calls?
	 */
	@Override
	protected int next(int bits) {
		step();
		// Hate the dumb mask
		return (int) (((counter + stepped) >>> 31) & ((1l << bits) - 1));
	}

	@Override
	public void setSeed(long seed) {
		counter =seed;
		if (counter == 0)
			counter =1;
                stepped=0;
                step();
                step();
                step();
                stepped=0;
	}

	/**
	 * uses only 32 bits of precision.
	 */
	@Override
	public double nextDouble() {
		step();
		return ((counter+stepped) & MASK_53)*LONG_2_DOUBLE;
	}


	@Override
	public long nextLong() {
		step();
		return counter+stepped;
	}
}